package cn.zwx.common.redisson.utils;

import cn.zwx.base.enums.ResponseCodeEnum;
import cn.zwx.base.exception.BizWrapException;
import cn.zwx.base.utils.SpelUtil;
import cn.zwx.common.redisson.annotation.RedissonLock;
import cn.zwx.common.redisson.enums.LockModelEnum;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.RedissonMultiLock;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
 * @author zhangwenxue
 */
public class LockUtil {

    @Resource
    private  RedissonClient redissonClient;

    public RLock getLock(ProceedingJoinPoint proceedingJoinPoint, RedissonLock lock, LockModelEnum lockModel) throws BizWrapException {
        String[] keys = lock.keys();
        if (keys.length == 0) {
            throw new BizWrapException(ResponseCodeEnum.REDISSON_LOCK_KEY_ERROR);
        }
        // 获取所有参数名称
        String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) proceedingJoinPoint.getSignature()).getMethod());
        // 获取参数列表
        Object[] args = proceedingJoinPoint.getArgs();
        final String keyExtension = lock.keyExtension();
        RLock rLock;
        //创建锁对象.
        switch (lockModel) {
            case FAIR:
                rLock = redissonClient.getFairLock(keyExtension+SpelUtil.getValeBySpel(keys[0],parameterNames,args).get(0));
                break;
            case RED_LOCK:
                List<RLock> rLocks=new ArrayList<>();
                for (String key : keys) {
                    List<String> vauleBySpel = SpelUtil.getValeBySpel(key, parameterNames, args);
                    for (String s : vauleBySpel) {
                        rLocks.add(redissonClient.getLock(keyExtension+s));
                    }
                }
                RLock[] locks =new RLock[rLocks.size()];
                int index=0;
                for (RLock r : rLocks) {
                    locks[index++]=r;
                }
                rLock = new RedissonRedLock(locks);
                break;
            case MULTIPLE:
                rLocks=new ArrayList<>();
                for (String key : keys) {
                    List<String> vauleBySpel = SpelUtil.getValeBySpel(key, parameterNames, args);
                    for (String s : vauleBySpel) {
                        rLocks.add(redissonClient.getLock(keyExtension+s));
                    }
                }
                locks=new RLock[rLocks.size()];
                index=0;
                for (RLock r : rLocks) {
                    locks[index++]=r;
                }
                rLock = new RedissonMultiLock(locks);
                break;
            case REENTRANT:
                List<String> vauleBySpel = SpelUtil.getValeBySpel(keys[0], parameterNames, args);
                //如果spel表达式是数组或者LIST 则使用红锁
                if(vauleBySpel.size()==1){
                    rLock= redissonClient.getLock(vauleBySpel.get(0));
                }else {
                    locks=new RLock[vauleBySpel.size()];
                    index=0;
                    for (String s : vauleBySpel) {
                        locks[index++]=redissonClient.getLock(keyExtension+s);
                    }
                    rLock = new RedissonRedLock(locks);
                }
                break;
            case READ:
                RReadWriteLock rReadWriteLock = redissonClient.getReadWriteLock(keyExtension+SpelUtil.getValeBySpel(keys[0],parameterNames,args).get(0));
                rLock = rReadWriteLock.readLock();
                break;
            case WRITE:
                RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(keyExtension+SpelUtil.getValeBySpel(keys[0],parameterNames,args).get(0));
                rLock = readWriteLock.writeLock();
                break;
            default:
                throw new BizWrapException(ResponseCodeEnum.REDISSON_LOCK_TYPE_ERROR);
        }
        return rLock;
    }


    /**
     *
     * @param lockModel 锁的模式
     * @param keys keys
     * @return 返回锁对象
     * @throws BizWrapException 业务异常
     */
    public  RLock getLock(LockModelEnum lockModel, String... keys) throws BizWrapException {
        if (keys == null || keys.length == 0) {
            throw new BizWrapException(ResponseCodeEnum.REDISSON_LOCK_KEY_ERROR);
        }
        RLock rLock;
        //创建锁对象.
        switch (getAutoLockModel(lockModel,keys)) {
            case FAIR:
                rLock = redissonClient.getFairLock(keys[0]);
                break;
            case RED_LOCK:
                List<RLock> rLocks=new ArrayList<>();
                for (String key : keys) {
                    rLocks.add(redissonClient.getLock(key));
                }
                RLock[] locks =new RLock[rLocks.size()];
                int index=0;
                for (RLock r : rLocks) {
                    locks[index++]=r;
                }
                rLock = new RedissonRedLock(locks);
                break;
            case MULTIPLE:
                rLocks=new ArrayList<>();
                for (String key : keys) {
                    rLocks.add(redissonClient.getLock(key));
                }
                locks=new RLock[rLocks.size()];
                index=0;
                for (RLock r : rLocks) {
                    locks[index++]=r;
                }
                rLock = new RedissonMultiLock(locks);
                break;
            case REENTRANT:
                //如果spel表达式是数组或者LIST 则使用红锁
                if(keys.length==1){
                    rLock= redissonClient.getLock(keys[0]);
                }else {
                    locks=new RLock[keys.length];
                    index=0;
                    for (String key : keys) {
                        locks[index++]=redissonClient.getLock(key);
                    }
                    rLock = new RedissonRedLock(locks);
                }
                break;
            case READ:
                RReadWriteLock rReadWriteLock = redissonClient.getReadWriteLock(keys[0]);
                rLock = rReadWriteLock.readLock();
                break;
            case WRITE:
                RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(keys[0]);
                rLock = readWriteLock.writeLock();
                break;
            default:
                throw new BizWrapException(ResponseCodeEnum.REDISSON_LOCK_TYPE_ERROR);
        }
        return rLock;
    }


    public static LockModelEnum getAutoLockModel(RedissonLock lock, String[] keys) {
        LockModelEnum lockModel = lock.lockModel();
        return getAutoLockModel(lockModel,keys);
    }
    private static LockModelEnum getAutoLockModel(LockModelEnum lockModel, String[] keys) {
        if (lockModel.equals(LockModelEnum.AUTO)) {
            if (keys.length > 1) {
                lockModel = LockModelEnum.RED_LOCK;
            } else {
                lockModel = LockModelEnum.REENTRANT;
            }
        }
        return lockModel;
    }

    public static void processUnlock(RLock rLock) {
        // RedissonRedLock 没有实现 isLocked isHeldByCurrentThread
        if (rLock instanceof RedissonRedLock){
            // 释放锁
            rLock.unlock();
        }else {
            // 是否还是锁定状态
            if(rLock.isLocked()){
                // 时候是当前执行线程的锁
                if(rLock.isHeldByCurrentThread()){
                    // 释放锁
                    rLock.unlock();
                }
            }
        }
    }
}
